/**
 *  This file is part of PennerBot
 *
 *  Copyright (C) 2010 microkernel
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <core/convert.hpp>
#include <core/portable.hpp>

#include <console/statuslistener.hpp>

namespace PBot
{
namespace Console
{

StatusListener* StatusListener::instance = NULL;

StatusListener::StatusListener()
 : stopRequested(false)
{
}

StatusListener::~StatusListener()
{
    for(std::vector<Log*>::const_iterator ito = logs.begin(); ito != logs.end(); ++ito)
    {
        delete *ito;
    }
}

StatusListener& StatusListener::GetInstance()
{
    if(!instance)
    {
        instance = new StatusListener();
    }

    return *instance;
}

void StatusListener::Destroy()
{
    delete instance;
    instance = NULL;
}

void StatusListener::Initialize(const std::vector<Core::Config*>& cfgs)
{
    for(std::vector<Core::Config*>::const_iterator ito = cfgs.begin(); ito != cfgs.end(); ++ito)
    {
        Log* log = new Log((*ito)->botname, io_mutex);
        logs.push_back(log);
    }
}

void StatusListener::StartListener()
{
    thread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&StatusListener::Listen, this)));
}

void StatusListener::Dispatch(unsigned int id, Messages status, pvariant v)
{
    message msg(id, status, v);

    {
        boost::recursive_mutex::scoped_lock lock(mutex);
        messages.push(msg);
    }
}

void StatusListener::Listen()
{
    message msg;

    unsigned int id;
    Messages status;
    pvariant data;

    unsigned int num = 0;

    while(!stopRequested || num > 0)
    {
        {
            boost::recursive_mutex::scoped_lock lock(mutex);
            num = messages.size();
        }

        if(num > 0)
        {
            {
                boost::recursive_mutex::scoped_lock lock(mutex);

                msg = messages.front();
                messages.pop();
                --num;
            }

            id = msg.get<0>();
            status = msg.get<1>();
            data = msg.get<2>();

            switch(status)
            {
                /* BOT CORE */

                case MSG_BOT_STARTED:
                    logs[id]->Info("Bot started");
                    break;

                case MSG_BOT_LOGINTRY:
                    logs[id]->Info(std::string("Login attempt ") + Core::NumToStr(boost::get<unsigned int>(data)+1) + std::string("..."));
                    break;

                case MSG_BOT_LOGINCHANGE:
                    logs[id]->Info("Login form has been changed");
                    break;

                case MSG_BOT_LOGINATTEMPTS:
                    logs[id]->Info("Too many failed login attempts - sleeping 10min");
                    break;

                case MSG_BOT_WRONGPASS:
                    logs[id]->Info("Wrong username and/or password");
                    break;

                case MSG_BOT_LOGGEDIN:
                    logs[id]->Info("Logged in");
                    break;

                case MSG_BOT_ALREADYLOGGEDIN:
                    logs[id]->Info("Already logged in");
                    break;

                case MSG_BOT_NOMODULES:
                    logs[id]->Info("No active modules - disabling bot");
                    break;

                case MSG_BOT_MODULEDISABLED:
                    logs[id]->Info("Disabling " + boost::get<std::string>(data) + " module because of too many failed attempts");
                    break;

                /* CONNECTION */

                case MSG_CON_INITFAIL:
                    logs[id]->Error(std::string("cURL initialization failed"));
                    break;

                case MSG_CURL_ERROR:
                    logs[id]->Error(std::string("cURL message: ") + boost::get<std::string>(data));
                    break;

                case MSG_CON_ERROR:
                    logs[id]->Info(std::string("Error while loading url: ") + boost::get<std::string>(data));
                    break;

                case MSG_CURL_ESCAPEERR:
                    logs[id]->Info(std::string("Error while escaping string: ") + boost::get<std::string>(data));
                    break;

                /* UPDATECHECK */

                case MSG_UC_UPTODATE:
                    logs[id]->Info("This version of PennerBot is up to date und safe to use");
                    break;

                case MSG_UC_UNSAFE:
                    logs[id]->Info("This version of PennerBot is not safe anymore");
                    logs[id]->Info("Look at http://pb.kicks-ass.org for new versions");
                    break;

                case MSG_UC_ERROR:
                    logs[id]->Info("Error while checking for updates");
                    break;

                case MSG_UC_OLD:
                    logs[id]->Info("This version of PennerBot is outdated. Get the new one at http://pb.kicks-ass.org");
                    break;

                case MSG_UC_UNKNOWN:
                    logs[id]->Info("This version of PennerBot could not be found. Use it at your own risk.");
                    break;

                /* ANTICAPTCHA */

                case MSG_AC_IOERROR:
                    logs[id]->Error(std::string("[AC] Error opening captcha image: ") + boost::get<std::string>(data));
                    break;

                case MSG_AC_CRACKFAIL:
                    logs[id]->Info(std::string("[AC] Could not crack captcha - trying again"));
                    break;

                /* BOTTLECOLLECT */

                case MSG_BC_ENABLED:
                    logs[id]->Info("[BC] BottleCollect enabled");
                    break;

                case MSG_BC_DISABLED:
                    logs[id]->Info("[BC] BottleCollect disabled");
                    break;

                case MSG_BC_STARTING:
                    logs[id]->Info("[BC] Trying to start collecting");
                    break;

                case MSG_BC_COUNTERCHD:
                    logs[id]->Info("[BC] Counter regex did not match - trying again");
                    break;

                case MSG_BC_FORMCHD:
                    logs[id]->Info("[BC] Form regex did not match - trying again");
                    break;

                case MSG_BC_CAPTCHACHD:
                    logs[id]->Info("[BC] Captcha regex did not match - trying again");
                    break;

                case MSG_BC_ALREADYCOLL:
                    logs[id]->Info("[BC] Already collecting - waiting " + Core::NumToStr(boost::get<unsigned int>(data)) + " seconds");
                    break;

                case MSG_BC_FIGHTING:
                    logs[id]->Info("[BC] Cannot start to collect while fighting - waiting " + Core::NumToStr(boost::get<unsigned int>(data)) + " seconds");
                    break;

                case MSG_BC_CRIME:
                    logs[id]->Info("[BC] Already committing a crime - waiting " + Core::NumToStr(boost::get<unsigned int>(data)) + " seconds");
                    break;

                case MSG_BC_STARTED:
                    logs[id]->Info("[BC] Started to collect - waiting " + Core::NumToStr(boost::get<unsigned int>(data)) + " seconds");
                    break;

                case MSG_BC_TOOMANYATTEMPTS:
                    logs[id]->Info("[BC] Too many failed attempts - sleeping 10min");
                    break;

                case MSG_BC_CONCENTRATION:
                    logs[id]->Info("[BC] Concentration enabled - collecting for 60min");
                    break;

                case MSG_BC_THIRTYMIN:
                    logs[id]->Info("[BC] Going to collect for 30min");
                    break;

                case MSG_BC_TRYCHANGEJUNK:
                    logs[id]->Info("[BC] Trying to change junk");
                    break;

                case MSG_BC_JUNKNOTFOUND:
                    logs[id]->Info("[BC] Could not find engine");
                    break;

                case MSG_BC_ALREADYJUNK:
                    logs[id]->Info("[BC] Already wearing engine");
                    break;

                case MSG_BC_CHANGEDJUNK:
                    logs[id]->Info("[BC] Changed junk to engine");
                    break;

                case MSG_BC_CHANGEDJUNKBACK:
                    logs[id]->Info("[BC] Changed back to old junk");
                    break;

                case MSG_BC_JUNKFORMACTCHD:
                    logs[id]->Info("[BC] Junk form action regex did not match - trying again");
                    break;

                case MSG_BC_JUNKFORMIDCHD:
                    logs[id]->Info("[BC] Junk form id regex did not match - trying again");
                    break;

                case MSG_BC_JUNKIDCHD:
                    logs[id]->Info("[BC] Junk id regex did not match - trying again");
                    break;

                case MSG_BC_EMPTYOUTCART:
                    logs[id]->Info("[BC] Emptying out cart");
                    break;

                case MSG_BC_CARTRXFAIL:
                    logs[id]->Info("[BC] Shopping cart form regex failed");
                    break;

                /* BOTTLESELL */

                case MSG_BS_ENABLED:
                    logs[id]->Info("[BS] BottleSell enabled");
                    break;

                case MSG_BS_DISABLED:
                    logs[id]->Info("[BS] BottleSell disabled");
                    break;

                case MSG_BS_CONTAINERSIZEFAIL:
                    logs[id]->Info("[BS] Could not determine container size");
                    break;

                case MSG_BS_MONEYFAIL:
                    logs[id]->Info("[BS] Could not determine amount of money");
                    break;

                case MSG_BS_EXCHANGERATEFAIL:
                    logs[id]->Info("[BS] Could not determine exchange rate");
                    break;

                case MSG_BS_BOTTLECOUNTFAIL:
                    logs[id]->Info("[BS] Could not determine number of bottles");
                    break;

                case MSG_BS_ACTIONFAIL:
                    logs[id]->Info("[BS] Could not determine action value");
                    break;

                case MSG_BS_CHKVALFAIL:
                    logs[id]->Info("[BS] Could not determine chkval value");
                    break;

                case MSG_BS_FORMFAIL:
                    logs[id]->Info("[BS] Form changed");
                    break;

                case MSG_BS_RATECHANGED:
                    logs[id]->Info("[BS] Exchange rate changed to " + boost::get<std::string>(data));
                    break;

                case MSG_BS_NOBOTTLES:
                    logs[id]->Info("[BS] No bottles to sell");
                    break;

                case MSG_BS_MAXREACHED:
                    logs[id]->Info("[BS] Reached max money value or container is full");
                    break;

                case MSG_BS_SOLDBOTTLES:
                    logs[id]->Info("[BS] Sold bottles and got " + boost::get<std::string>(data));
                    break;

                case MSG_BS_TOOMANYATTEMPTS:
                    logs[id]->Info("[BS] Too many failed attempts - sleeping 10min");
                    break;

#ifdef PBOT_SPECIAL

                /* AUTOSKILL */

                case MSG_AS_ENABLED:
                    logs[id]->Info("[AS] AutoSkill enabled");
                    break;

                case MSG_AS_DISABLED:
                    logs[id]->Info("[AS] AutoSkill disabled");
                    break;

                case MSG_AS_NOSKILLS:
                    logs[id]->Info("[AS] No skills added - going to sleep");
                    break;

                case MSG_AS_CAPTCHACHD:
                    logs[id]->Info("[AS] Captcha regex did not match - trying again");
                    break;

                case MSG_AS_SKILLCOUNTERFAIL:
                    logs[id]->Info("[AS] Skill counter regex did not match");
                    break;

                case MSG_AS_ALREADYEDUCATING:
                    logs[id]->Info("[AS] Already educating - waiting " + Core::NumToStr(boost::get<unsigned int>(data)) + " seconds");
                    break;

                case MSG_AS_DEPENDENCY:
                    logs[id]->Info("[AS] Unresolved dependencies for " + boost::get<std::string>(data) + ". (Not enough money or skills)");
                    break;

                case MSG_AS_TRYING:
                    logs[id]->Info("[AS] Trying to start following skill: " + boost::get<std::string>(data));
                    break;

                case MSG_AS_STARTED:
                    logs[id]->Info("[AS] Started to educate - waiting " + Core::NumToStr(boost::get<unsigned int>(data)) + " seconds");
                    break;

                case MSG_AS_TOOMANYATTEMPTS:
                    logs[id]->Info("[AS] Too many failed attempts - sleeping 10min");
                    break;

#endif /* PBOT_SPECIAL */

            }
        }

        PortableMicroSleep(10);
    }
}

unsigned int StatusListener::NumMessages()
{
    boost::recursive_mutex::scoped_lock lock(mutex);
    return messages.size();
}

void StatusListener::RequestStop()
{
    stopRequested = true;
    thread->join();
}

}
}

